package com.easylinkin.linkappapi.grid.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.core.models.Operator;
import com.easylinkin.core.utils.HttpFileUtils;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.exceptions.ExceptionEnum;
import com.easylinkin.linkappapi.grid.entity.GridReportDay;
import com.easylinkin.linkappapi.grid.enums.EGridFileBusSubTypeReportDay;
import com.easylinkin.linkappapi.grid.enums.EGridFileBusType;
import com.easylinkin.linkappapi.grid.mapper.GridReportDayMapper;
import com.easylinkin.linkappapi.grid.models.dto.GridReportDaySaveDTO;
import com.easylinkin.linkappapi.grid.models.dto.GridReportDayUserSaveDTO;
import com.easylinkin.linkappapi.grid.models.query.GridFileBatchQuery;
import com.easylinkin.linkappapi.grid.models.query.GridReportDayQuery;
import com.easylinkin.linkappapi.grid.models.vo.GridFileInfoVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportDayInfoVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportDayListVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportDayUserInfoVO;
import com.easylinkin.linkappapi.grid.service.IGridFileService;
import com.easylinkin.linkappapi.grid.service.IGridReportDayService;
import com.easylinkin.linkappapi.grid.service.IGridReportDayUserService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 网格日报表 服务实现类
 *
 * @author kuyujie
 * @since 2024-05-16
 */
@Slf4j
@Setter(onMethod_ = @Resource)
@Validated
@Service("gridReportDayService")
public class GridReportDayServiceImpl extends ServiceImpl<GridReportDayMapper, GridReportDay> implements IGridReportDayService {

    private IGridFileService gridFileService;
    private IGridReportDayUserService gridReportDayUserService;

    @Override
    public IPage<GridReportDayListVO> pageList(IPage<?> page, @Validated GridReportDayQuery query) {
        return baseMapper.pageList(page, query);
    }

    @Override
    public byte[] export(GridReportDayQuery query) {
        List<GridReportDayListVO> list = baseMapper.listByQuery(query);
        if (CollUtil.isEmpty(list)) {
            return new byte[0];
        }

        Set<Long> reportIds = list.stream().map(GridReportDayListVO::getId).collect(Collectors.toSet());

        Map<String, List<GridFileInfoVO>> fileInfoListMap = gridFileService.mapListInfoByBatchQuery(GridFileBatchQuery.builder()
                .busIds(reportIds)
                .busType(EGridFileBusType.REPORT_DAY.getCode())
                .build()
        );

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);

        list.parallelStream().forEach(item -> {
            try {
                // 附件
                String attachmentKey = item.getId() + "," + EGridFileBusType.REPORT_DAY.getCode() + "," + EGridFileBusSubTypeReportDay.ATTACHMENT.getCode();
                List<GridFileInfoVO> attachmentFileInfoList = fileInfoListMap.get(attachmentKey);
                if (CollUtil.isNotEmpty(attachmentFileInfoList)) {
                    for (GridFileInfoVO fileInfoVO : attachmentFileInfoList) {
                        try {
                            HttpResponse httpResponse = HttpFileUtils.requestDownload(fileInfoVO.getFileUrl(), 1000);
                            zipOutputStream.putNextEntry(new ZipEntry(item.getReportName() + File.separator + "附件" + File.separator + fileInfoVO.getFileName()));
                            zipOutputStream.write(httpResponse.bodyBytes());
                            zipOutputStream.closeEntry();
                        } catch (Exception e) {
                            log.error("下载文件失败 fileInfoVO:{}", JSON.toJSONString(fileInfoVO), e);
                        }
                    }
                }

                // 签字附件
                String signAttachmentKey = item.getId() + "," + EGridFileBusType.REPORT_DAY.getCode() + "," + EGridFileBusSubTypeReportDay.SIGN_ATTACHMENT.getCode();
                List<GridFileInfoVO> signAttachmentKeyFileInfoList = fileInfoListMap.get(signAttachmentKey);
                if (CollUtil.isNotEmpty(signAttachmentKeyFileInfoList)) {
                    for (GridFileInfoVO fileInfoVO : signAttachmentKeyFileInfoList) {
                        try {
                            HttpResponse httpResponse = HttpFileUtils.requestDownload(fileInfoVO.getFileUrl(), 1000);
                            zipOutputStream.putNextEntry(new ZipEntry(item.getReportName() + File.separator + "签字附件" + File.separator + fileInfoVO.getFileName()));
                            zipOutputStream.write(httpResponse.bodyBytes());
                            zipOutputStream.closeEntry();
                        } catch (Exception e) {
                            log.error("下载文件失败", e);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("导出失败 item:{}", JSON.toJSONString(item), e);
            }
        });

        // 总的zip ZipOutputStream close
        try {
            zipOutputStream.flush();
            //解决CRS校验失败问题
            zipOutputStream.finish();
            zipOutputStream.close();
            byteArrayOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return byteArrayOutputStream.toByteArray();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveData(@Validated GridReportDaySaveDTO dto) {
        Operator operator = dto.getOperator();
        List<GridReportDayUserSaveDTO> userList = dto.getUserList();
        String reportName = DateUtil.format(dto.getSubmitDeadline(), DatePattern.CHINESE_DATE_PATTERN) + "网格日报";

        int existFlag = baseMapper.existByDate(operator.getTenantId(), dto.getSubmitDeadline());
        if (existFlag > 0) {
            log.error("日报设置重复");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.DATA_CHECK_ERROR, "日报设置重复");
        }

        GridReportDay entity = GridReportDay.builder()
                .reportName(reportName)
                .submitDeadline(dto.getSubmitDeadline())
                .requiresSubmitNum(userList.size())
                .alreadySubmitNum(0)
                // 公共字段
                .tenantId(operator.getTenantId())
                .creatorId(operator.getUserId())
                .creator(operator.getNickname())
                .createTime(operator.getNow())
                .modifyId(operator.getUserId())
                .modifier(operator.getNickname())
                .modifyTime(operator.getNow())
                .build();

        boolean dbFlag = this.save(entity);
        if (!dbFlag) {
            log.error("日报设置数据保存失败");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.EXCEPTION_CODE_500, "日报设置数据保存失败");
        }

        dbFlag = gridReportDayUserService.saveData(entity, userList);
        if (!dbFlag) {
            log.error("日报填报人数据保存失败");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.EXCEPTION_CODE_500, "日报填报人数据保存失败");
        }
    }

    @Override
    public GridReportDayInfoVO getInfo(Long id) {
        GridReportDayInfoVO infoVO = baseMapper.selectInfoById(id);
        List<GridReportDayUserInfoVO> userList = gridReportDayUserService.listByReportId(id, infoVO.getSubmitDeadline());
        infoVO.setUserList(userList);
        return infoVO;
    }

}